package cn.tomoya.module.security;

import java.io.Serializable;
import java.util.Date;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import cn.tomoya.common.config.SiteConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenUtil implements Serializable
{

    private static final long serialVersionUID = -3301605591108950415L;

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_AUDIENCE = "audience";
    private static final String CLAIM_KEY_CREATED = "created";
    private static final String CLAIM_KEY_USER = "user";

    private static final String AUDIENCE_UNKNOWN = "unknown";
    private static final String AUDIENCE_WEB = "web";
    private static final String AUDIENCE_MOBILE = "mobile";
    private static final String AUDIENCE_TABLET = "tablet";
    @Autowired
    private SiteConfig siteConfig;
    /**
     * 重新生成token，主要用于延长失效时间
     * @param token
     * @return 新的token
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            final Claims claims = getClaimsFromToken(token);
            claims.put(CLAIM_KEY_CREATED, new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 校验token的有效性
     * @param token
     * @param userDetails
     * @return 是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        JwtUser user = (JwtUser) userDetails;
        /*final String username = getUsernameFromToken(token);*/



        final Date created = getCreatedDateFromToken(token);
        final Date expiration = getExpirationDateFromToken(token);
        System.out.println(expiration+"++++++exp");
        return (/*username.equals(user.getUsername()) &&*/!isTokenExpired(token)
                && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()));
    }

    /**
     * @param token
     * @param lastPasswordReset
     * @return token是否可以更新，移动端不受失效时间限制
     */
    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
                && (!isTokenExpired(token) || ignoreTokenExpiration(token));
    }

    /**
     * @param token
     * @return 从token中获得用户名，对应sub
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * @param token
     * @return token的创建时间
     */
    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    /**
     * @param token
     * @return token的失效时间
     */
    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    /**
     * @param token
     * @return token的用户信息，对应jwtuser
     */
    public JwtUser getUserFromToken(String token) {
        JwtUser user = new JwtUser();
        try {
            Map<String, Object> claims  = getClaimsFromToken(token);
            // TODO
            Map<String, Object> userMap =  (Map<String, Object>)claims.get(CLAIM_KEY_USER);
            user.setUsername((String)userMap.get("username"));
            user.setId((String)userMap.get("id"));
            user.setOrgName((String) userMap.get("orgName"));
            user.setNickName((String)userMap.get("nickName"));
        } catch (Exception e) {
            e.printStackTrace();
            user = null;
        }
        return user;
    }

    /**
     * @param token
     * @return token中的终端类型，对应audience
     */
    public String getAudienceFromToken(String token) {
        String audience;
        try {
            final Claims claims = getClaimsFromToken(token);
            audience = (String) claims.get(CLAIM_KEY_AUDIENCE);
        } catch (Exception e) {
            audience = null;
        }
        return audience;
    }

    public Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(siteConfig.getJwtSecret()).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    /**
     * 根据不同的终端 设置不同的超时时间<br>
     * web为1小时, 移动端为10天
     * @param device
     * @return
     */
    private Date generateExpirationDate(String device) {
//        if (device == null || AUDIENCE_WEB.equals(device))
//            return new Date(System.currentTimeMillis() + 12 * 3600 * 1000l);
//        else
            return new Date(System.currentTimeMillis() + 100 * 86400 * 1000l);
    }


    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }


    private Boolean ignoreTokenExpiration(String token) {
        String audience = getAudienceFromToken(token);
        return (AUDIENCE_TABLET.equals(audience) || AUDIENCE_MOBILE.equals(audience));
    }

    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder().setClaims(claims)
                .setExpiration(generateExpirationDate((String) claims.get(CLAIM_KEY_AUDIENCE)))
                .signWith(SignatureAlgorithm.HS512, siteConfig.getJwtSecret()).compact();
    }
}